/**
 * 基础API服务类
 * 提供通用的CRUD操作和常用方法
 * 这个类是所有业务服务类的基类，封装了常用的数据操作方法
 */

// 导入依赖模块
// 导入API客户端实例，用于发送HTTP请求
import { apiClient } from './client.js';
// 导入API配置，用于获取默认配置值
import { API_CONFIG } from './config.js';

// 基础服务类定义
// 使用ES6 class语法，提供面向对象的服务封装
export class BaseService {
  /**
   * 构造函数
   * 初始化服务实例的端点和选项
   * @param {string} endpoint - API端点路径，如'/projects'
   * @param {object} options - 服务选项配置
   * @param {boolean} options.showToast - 是否显示成功/错误提示，默认true
   * @param {boolean} options.silent - 是否静默处理（不显示toast），默认false
   */
  constructor(endpoint, options = {}) {
    // 设置API端点路径，用于构建完整的请求URL
    this.endpoint = endpoint;
    
    // 合并默认选项和传入的选项
    // 使用展开运算符，后面的选项会覆盖前面的同名选项
    this.options = {
      showToast: true,    // 默认显示toast提示
      silent: false,      // 默认不静默处理
      ...options,         // 展开传入的选项，覆盖默认值
    };
  }

  /**
   * 构建查询参数
   * 标准化和验证查询参数，确保分页、排序等参数符合规范
   * @param {object} params - 原始查询参数对象
   * @param {number} params.page - 页码，默认从1开始
   * @param {number} params.pageSize - 每页数量，默认10
   * @param {string} params.sortBy - 排序字段，默认'created_at'
   * @param {string} params.sortOrder - 排序方向，默认'desc'
   * @param {string} params.search - 搜索关键词
   * @param {object} params.filters - 额外的筛选条件
   * @returns {object} 处理后的标准化查询参数
   */
  buildQueryParams(params = {}) {
    // 使用解构赋值获取参数，并设置默认值
    const {
      page = API_CONFIG.PAGINATION.defaultPage,           // 默认页码：1
      pageSize = API_CONFIG.PAGINATION.defaultPageSize,   // 默认每页数量：10
      sortBy = 'created_at',                              // 默认排序字段：创建时间
      sortOrder = 'desc',                                 // 默认排序方向：降序
      search = '',                                        // 默认搜索关键词：空字符串
      filters = {},                                       // 默认筛选条件：空对象
      ...otherParams                                      // 其他未明确声明的参数
    } = params;

    // 返回标准化的查询参数对象
    return {
      // 确保页码至少为1，使用parseInt转换为整数
      page: Math.max(1, parseInt(page)),
      
      // 确保每页数量在合理范围内
      // 外层Math.min限制最大值为配置的最大值
      // 内层Math.max确保最小值为1
      pageSize: Math.min(
        API_CONFIG.PAGINATION.maxPageSize,  // 最大每页数量限制
        Math.max(1, parseInt(pageSize))     // 最小每页数量为1
      ),
      
      // 排序相关参数
      sortBy,      // 排序字段
      sortOrder,   // 排序方向
      
      // 搜索关键词
      search,
      
      // 展开筛选条件和其他参数
      // 使用展开运算符将所有额外参数合并到返回对象中
      ...filters,      // 展开筛选条件
      ...otherParams,  // 展开其他参数
    };
  }

  /**
   * 获取列表数据
   * 通用的列表查询方法，支持分页、排序、搜索等功能
   * @param {object} params - 查询参数对象
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 包含列表数据和分页信息的响应对象
   */
  async getList(params = {}, options = {}) {
    // 使用buildQueryParams方法标准化查询参数
    const queryParams = this.buildQueryParams(params);
    
    // 调用API客户端发送GET请求
    return apiClient.get(this.endpoint, {
      query: queryParams,        // 设置查询参数
      ...this.options,           // 展开服务默认选项
      ...options,                // 展开传入的选项，覆盖默认选项
    });
  }

  /**
   * 获取详情数据
   * 根据ID获取单个记录的详细信息
   * @param {string|number} id - 记录的唯一标识符
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 记录详情数据
   */
  async getDetail(id, options = {}) {
    // 构建详情接口URL：基础端点 + /ID
    return apiClient.get(`${this.endpoint}/${id}`, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 创建新记录
   * 向服务器提交新数据创建记录
   * @param {object} data - 要创建的数据对象
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 创建成功后的记录数据
   */
  async create(data, options = {}) {
    // 调用API客户端发送POST请求创建记录
    return apiClient.post(this.endpoint, data, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 完整更新记录
   * 使用PUT方法完整替换记录的所有数据
   * @param {string|number} id - 记录的唯一标识符
   * @param {object} data - 完整的更新数据对象
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 更新成功后的记录数据
   */
  async update(id, data, options = {}) {
    // 调用API客户端发送PUT请求，完整更新记录
    return apiClient.put(`${this.endpoint}/${id}`, data, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 部分更新记录
   * 使用PATCH方法只更新指定的字段，其他字段保持不变
   * @param {string|number} id - 记录的唯一标识符
   * @param {object} data - 部分更新数据对象，只包含需要更新的字段
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 更新成功后的记录数据
   */
  async patch(id, data, options = {}) {
    // 调用API客户端发送PATCH请求，部分更新记录
    return apiClient.patch(`${this.endpoint}/${id}`, data, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 删除记录
   * 根据ID删除指定的记录
   * @param {string|number} id - 要删除的记录的唯一标识符
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 删除操作的结果信息
   */
  async delete(id, options = {}) {
    // 调用API客户端发送DELETE请求删除记录
    return apiClient.delete(`${this.endpoint}/${id}`, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 批量删除记录
   * 一次性删除多个记录，提高操作效率
   * @param {Array<string|number>} ids - 要删除的记录ID数组
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 批量删除操作的结果信息
   */
  async batchDelete(ids, options = {}) {
    // 调用API客户端发送POST请求到批量删除端点
    // 请求体包含要删除的ID数组
    return apiClient.post(`${this.endpoint}/batch-delete`, { ids }, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 批量更新记录
   * 一次性更新多个记录，提高操作效率
   * @param {Array<object>} updates - 更新数据数组，每个元素包含id和要更新的数据
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 批量更新操作的结果信息
   */
  async batchUpdate(updates, options = {}) {
    // 调用API客户端发送POST请求到批量更新端点
    // 请求体包含更新数据数组
    return apiClient.post(`${this.endpoint}/batch-update`, { updates }, {
      ...this.options,  // 展开服务默认选项
      ...options,       // 展开传入的选项
    });
  }

  /**
   * 获取统计数据
   * 获取当前资源的统计信息，如总数、分类统计等
   * @param {object} params - 统计查询参数，如时间范围、筛选条件等
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 统计数据对象
   */
  async getStats(params = {}, options = {}) {
    // 调用API客户端发送GET请求到统计端点
    return apiClient.get(`${this.endpoint}/stats`, {
      query: params,      // 设置查询参数
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }

  /**
   * 搜索数据
   * 根据关键词搜索相关记录
   * @param {string} keyword - 搜索关键词
   * @param {object} params - 额外的搜索参数，如分页、排序等
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 搜索结果，包含匹配的记录列表和分页信息
   */
  async search(keyword, params = {}, options = {}) {
    // 调用getList方法，将搜索关键词添加到查询参数中
    return this.getList({
      ...params,        // 展开额外的搜索参数
      search: keyword,  // 设置搜索关键词
    }, options);
  }

  /**
   * 检查记录是否存在
   * 通过尝试获取记录详情来判断记录是否存在
   * @param {string|number} id - 记录的唯一标识符
   * @param {object} options - 请求选项配置
   * @returns {Promise<boolean>} 记录是否存在，true表示存在，false表示不存在
   */
  async exists(id, options = {}) {
    try {
      // 尝试获取记录详情，使用silent模式避免显示错误提示
      await this.getDetail(id, { ...options, silent: true });
      // 如果没有抛出错误，说明记录存在
      return true;
    } catch (error) {
      // 如果错误状态码是404，说明记录不存在
      // 其他错误（如网络错误、权限错误等）返回true，因为不确定记录是否存在
      return error.status !== 404;
    }
  }

  /**
   * 获取下拉选项数据
   * 获取用于下拉列表、选择器等UI组件的选项数据
   * @param {object} params - 查询参数，如分类筛选、状态筛选等
   * @param {object} options - 请求选项配置
   * @returns {Promise<Array>} 选项数据数组，通常包含id、name等字段
   */
  async getOptions(params = {}, options = {}) {
    // 调用API客户端发送GET请求到选项端点
    return apiClient.get(`${this.endpoint}/options`, {
      query: params,      // 设置查询参数
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }

  /**
   * 导出数据
   * 将数据导出为文件格式（如Excel、CSV等）
   * @param {object} params - 导出参数，如筛选条件、导出格式等
   * @param {object} options - 请求选项配置
   * @returns {Promise<Blob>} 导出文件的Blob对象，可用于下载
   */
  async export(params = {}, options = {}) {
    // 调用API客户端发送GET请求到导出端点
    // 返回的是文件Blob对象，不是JSON数据
    return apiClient.get(`${this.endpoint}/export`, {
      query: params,      // 设置导出参数
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }

  /**
   * 导入数据
   * 从文件导入数据到系统中
   * @param {File} file - 要导入的文件对象
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 导入操作的结果信息，包含成功数量、失败数量等
   */
  async import(file, options = {}) {
    // 调用API客户端的文件上传方法导入数据
    return apiClient.uploadFile(`${this.endpoint}/import`, file, {
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }

  /**
   * 上传文件
   * 为指定记录上传关联文件
   * @param {string|number} id - 记录的唯一标识符
   * @param {File|FileList|Array<File>} files - 要上传的文件，可以是单个文件、文件列表或文件数组
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 文件上传结果，包含上传成功的文件信息
   */
  async uploadFiles(id, files, options = {}) {
    // 构建文件上传端点：基础端点/记录ID/files
    const endpoint = `${this.endpoint}/${id}/files`;
    
    // 调用API客户端的文件上传方法
    return apiClient.uploadFile(endpoint, files, {
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }

  /**
   * 删除文件
   * 删除指定记录关联的特定文件
   * @param {string|number} id - 记录的唯一标识符
   * @param {string} fileId - 要删除的文件ID
   * @param {object} options - 请求选项配置
   * @returns {Promise<object>} 文件删除操作的结果信息
   */
  async deleteFile(id, fileId, options = {}) {
    // 调用API客户端发送DELETE请求删除指定文件
    return apiClient.delete(`${this.endpoint}/${id}/files/${fileId}`, {
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }

  /**
   * 获取文件列表
   * 获取指定记录关联的所有文件信息
   * @param {string|number} id - 记录的唯一标识符
   * @param {object} options - 请求选项配置
   * @returns {Promise<Array>} 文件列表数组，包含文件的基本信息
   */
  async getFiles(id, options = {}) {
    // 调用API客户端发送GET请求获取文件列表
    return apiClient.get(`${this.endpoint}/${id}/files`, {
      ...this.options,    // 展开服务默认选项
      ...options,         // 展开传入的选项
    });
  }
}

// 导出BaseService类作为默认导出
// 这样其他模块可以直接导入并使用这个基础服务类
export default BaseService;
